package com.yunfinal.api.service;

import com.alibaba.fastjson.JSONObject;
import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.core.Controller;
import com.jfinal.core.JFinal;
import com.jfinal.kit.JsonKit;
import com.jfinal.log.Log;
import com.yunfinal.api.service.doc.ApiReportSocket;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 接口式开发，交互格式统一规划
 * 杜福忠 2019-02-18
 */
public class ApiHandleInterceptor implements Interceptor {

    /**
     * 日志
     */
    protected final Log log = Log.getLog(getClass());

    /**
     * 获得传承
     */
    public void intercept(Invocation inv){
        Controller c = inv.getController();
        HttpServletResponse response = c.getResponse();
        HttpServletRequest request = c.getRequest();
        // 设置头部信息
        setHeaders(response);
        if (isOptions(request)) {
            // 过滤跨域检测
            c.renderJson("{}");
            return;
        }
        // 开发模式 记录耗时情况
        long t = 0;
        if (JFinal.me().getConstants().getDevMode()) {
            t = System.currentTimeMillis();
        }
        // 解析处理
        JSONObject kv = null;
        try {
            kv = invokeData(inv);
        } catch (ApiException e) {
            kv = getRetMap(e.code, e.msg, e.data);
        } catch (Exception e) {
            log.error("ApiHandleController 业务异常: ", e);
            kv = getRetMap("500", e.getMessage(), null);
        }
        // 返回客户端
        String retJson = JsonKit.toJson(kv);
        c.renderJson(retJson);

        // 控制台打印报告
        report(inv, retJson, t);
    }

    /**
     * 判断请求是否 跨域检测
     *
     * @return
     */
    protected boolean isOptions(HttpServletRequest request) {
        return "OPTIONS".equals(request.getMethod());
    }

    /**
     * 封装返回值
     *
     * @param code
     * @param msg
     * @param data
     * @return
     */
    protected JSONObject getRetMap(String code, String msg, Object data) {
        JSONObject ret = new JSONObject();
        ret.put("code", code);
        ret.put("msg", msg);
        ret.put("data", data);
        return ret;
    }

    /**
     * 数据解析处理
     *
     * @return
     */
    protected JSONObject invokeData(Invocation inv) {
        // 回调处理
        inv.invoke();
        // 封装返回值
        return getRetMap("0", "OK", inv.getReturnValue());
    }

    protected void report(Invocation inv, String retJson, long t) {
        if (JFinal.me().getConstants().getDevMode()) {
            long l = System.currentTimeMillis();
            Controller c = inv.getController();
            StringBuilder msg = new StringBuilder(256);
            msg.append("API服务报告单 --------------------------\n");
            msg.append("方法: ").append(inv.getActionKey()).append("\n");
            msg.append("参数: ").append(c.getRawData()).append("\n");
            msg.append("返回: ").append(retJson).append("\n");
            msg.append("耗时: ").append(l - t).append("毫秒\n");
            msg.append("处理: ").append(getClass().getName());
            msg.append(".(").append(getClass().getSimpleName());
            msg.append(".java:1)");
            String message = msg.toString();
            log.info(message);
            ApiReportSocket.sendMsg(message);
        }
    }

    /**
     * 默认JSON返回 允许跨域
     */
    protected void setHeaders(HttpServletResponse response) {
        response.addHeader("Content-Type", "application/json; charset=utf-8");
        response.addHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Allow-Methods", "GET,POST");
        response.addHeader("Access-Control-Allow-Headers",
                "Origin,X-Requested-With,Content-Type,Accept");
    }

    /**
     * 错误直接返回
     *
     * @param code
     * @param msg
     */
    protected void returnError(String code, String msg) {
        throw new ApiException(code, msg);
    }

    /**
     * 错误直接返回
     *
     * @param code
     * @param msg
     * @param data
     */
    protected void returnError(String code, String msg, Object data) {
        throw new ApiException(code, msg, data);
    }

}
